#!/usr/bin/env python

import os
import sys
import select
import socket
import signal
import threading

class fileSystem:
    def open(self, args):
        ofile = open(args)
        open_files[args] = ofile
        return 'ok\n'

    # read : [file, size, offset]
    def read(self, args):
        # theoricaly no need to check ; already handled by the kernel
        ofile = open(args[0], 'r')
        size = int(args[1])
        offset = int(args[2])
        ofile.seek(offset)
        print 'reading', size, offset
        return ofile.read(size)

    # write : [file, size, offset, text]
    def write(self, args):
        ofile = open(args[0], 'r+b')
        size = int(args[1])
        offset = int(args[2])
        ofile.seek(offset) # ce connard veut pas revenir
        towrite = args[3][:size]
        print 'writing', towrite, size, offset
        ofile.write(towrite)
        ofile.flush()
        return str(size)
    
    def mkdir(self, args):
        os.mkdir(args[0])
        return 'ok\n'

    # create : [id, mode]
    def create(self, args):
        ofile = open(args[0], 'w+')
        ofile.close()
        return 'ok\n'

DEFAULT_PORT = 62000

class Server:
    def __init__(self, port = DEFAULT_PORT):
        self.port = port
        self.size = 4096
        self.backlog = 5
        self.server = None
        self.threads = []
        self.running = 0

    def sighandler(self, signum, frame):
        print '\nShutting down server...'
        self.close_clients()

    def open_socket(self):
        try:
            self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            self.server.bind(('', self.port))
            self.server.listen(self.backlog)
            signal.signal(signal.SIGINT, self.sighandler)
        except socket.error, (value,message):
            if self.server:
                self.server.close()
            print 'Error: could not open socket: ' + message
            sys.exit(1)

    def close_clients(self):
        for c in self.threads:
            c.running = 0
        for c in self.threads:
            c.join()
        self.running = 0
        if self.server:
            self.server.close()

    def run(self):
        self.open_socket()
        print '[Listening on port %d]' % self.port
        input = [self.server, sys.stdin]
        self.running = 1
        while self.running:
            try:
                inputready,outputready,exceptready = select.select(input, [], [], 1)
            except Exception, err:
                if self.server:
                    self.server.close()
                break

            for s in inputready:
                if s == self.server:
                    # handle the server socket
                    c = Client(self.server.accept())
                    c.start()
                    self.threads.append(c)
                elif s == sys.stdin:
                    # handle standard input
                    line = sys.stdin.readline()
                    # close all threads
                    if line == '':
                        print 'Exiting... please wait...'
                        self.close_clients()
                        break
                    else: # Broadcast message
                        for c in self.threads:
                            c.sock.send(line)


class Client(threading.Thread):
    def __init__(self, (client, address)):
        threading.Thread.__init__(self)
        self.sock = client
        self.address = address
        self.size = 4096
        self.input = [self.sock]
        self.running = 0
        self.fs = fileSystem()

    def run(self):
        self.running = 1
        print 'Client connected %d from %s' % (self.sock.fileno(), self.address)
        while self.running:
            try:
                inputready, outputready, execptrdy = select.select(self.input, [], [], 2)
                for i in inputready:
                    data = self.sock.recv(self.size)
                    if not data:
                        print 'Client disconnected %d from %s' % (self.sock.fileno(), self.address)
                        self.running = 0
                        self.sock.close()
                        break
                    else:
                        print 'Client('+str(self.sock.fileno())+') : '+data
                        #fs = fileSystem
                        ptrCmd = {
                            'open' : self.fs.open,
                            'read' : self.fs.read,
                            'write' : self.fs.write,
                            'mkdir' : self.fs.mkdir,
                            'create' : self.fs.create}
                        try:
                            cmds = data.split()
                            res = ptrCmd[cmds[0]](cmds[1:])
                            self.sock.send(res)
                        except Exception, e:
                            self.sock.send('exc:' + str(type(e)) + ':' + str(e) + '\n')
                            raise
            except Exception, err:
                print err
                self.sock.close()
                break

if __name__ == "__main__":
    s = Server()
    s.run()
